/* pmep.c
 *
 * pmem - Determining the amount of memory used by a process
 * Copyright (C) 2011 Ivan Totev <ivan.totev.85@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

#define LINE_BUF 1024
#define PROGRAM "pmem"
#define VERSION "0.1"

char *pmem(unsigned int pid, unsigned int verbose, unsigned int humanize);
void usage(void);

int main(int argc, char **argv) {
	FILE *fd;
	char *line;

	int opt, verbose, humanize;
	verbose=humanize=0;
	// Get options
	while((opt = getopt(argc, argv, "hvVH")) != -1) {
		switch(opt) {
			case 'H':
				usage();
			break;
			case 'V':
				printf("%s v%s\n", PROGRAM, VERSION); 
				exit(0);
			break;
			case 'v':
				++verbose;
			break;
			case 'h':
				humanize=1;
			break;
		}
	}

	unsigned int pid;
	int index;

	if ( optind >= argc ) usage();

	for (index = optind; index < argc; index++) {
		pid = atoi(argv[index]);
		if ( pid == 0 ) continue;
		if ( pmem(pid, verbose, humanize) == NULL ) {
			continue;
		}
	}

	exit(EXIT_SUCCESS);
}

void usage(void) {
	fprintf(stderr, "%s v%s\n", PROGRAM, VERSION);
	fprintf(stderr, "Usage: %s -[vhVH] pid\n", PROGRAM);
	exit(EXIT_FAILURE);
}

char *pmem(unsigned int pid, unsigned int verbose, unsigned int humanize) {
	FILE *fd;
	char procfile[256];

	snprintf(procfile, sizeof(procfile),"/proc/%d/maps", pid);

	if ( pid < 0 ) usage();

	char *line = malloc(LINE_BUF);
	if  ( line == NULL ) {
		fprintf(stderr, "Memory error\n");
		return 0;
	}

	fd = fopen(procfile,"r");
	if ( ! fd ) {
		return 0;
	}

	unsigned long long total_memory = 0;

	while ( fgets(line, LINE_BUF, fd) ) {
		char flags[32];
		char lib[256];
		unsigned int long used_memory;
		unsigned int long start, end;
		unsigned long long file_offset, inode;
		unsigned dev_major, dev_minor;
    		sscanf(line, "%lx-%lx %31s %Lx %x:%x %Lu %255s", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode, &lib);
		used_memory = (end - start) / 1024;

		switch(verbose) {
			case 0:
				break;
			case 1:
				printf("%s %dK\n", lib, used_memory);
			break;
			default:
				printf("%lx-%lx %s %Lx %x:%x %Lu %s %dK\n", start, end, flags, file_offset, dev_major, dev_minor, inode, lib, used_memory);
		}
		total_memory += used_memory;
	}

	printf("%d: ", pid);

	if ( humanize ) {
		if ( verbose ) printf("total: ");

		if ( total_memory > (1024*1024) ) {
			unsigned int mb = total_memory % ( 1024*1024 );
			unsigned int gb = total_memory / ( 1024 * 1024 );
			printf("%d.%dG\n", gb, mb);
			return 0;
		} else if ( total_memory > 1024 ) {
			unsigned int kb = total_memory % 1024;
			unsigned int mb = total_memory /  1024;
			printf("%d.%dM\n", mb, kb);
			return 0;
		} else {
			printf("%dK\n", total_memory);
		}
		return 0;
	}
	printf("%s%dK\n",(verbose) ? "total: " : "", total_memory);
	return 0;
}
